package com.jerry.usbserial.util.task.udp

import com.jerry.usbserial.util.*
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import java.net.SocketTimeoutException

/**
 * Created by dugang on 2020/7/7.
 */
class UDPMenuTask(host: String, private val mList: List<MenuItem>) : Thread() {
    companion object {
        private const val CLIENT_PORT = 9527
        private const val SERVER_PORT = 5021
        private const val RECEIVE_SIZE = 34
    }

    private val mServer: String = host.substring(0, host.lastIndexOf(".") + 1) + "255"
    private val mSocketClient = DatagramSocket(CLIENT_PORT).apply { soTimeout = 10000 }

    private val mSendSize = mList.size
    private var mSendIndex = 0
    private var processListener: ((process: Int) -> Unit)? = null
    private var resultListener: ((msg: String) -> Unit)? = null
    private var mCheck = true
    private var mCrcArray = byteArrayOf()
    private val mOkResult = "53656E6420546F2055617274204F6B21".hexToByteArray()

    @Volatile
    private var mCancel = false

    @Volatile
    private var mRetry = 0

    fun cancel() {
        mCancel = true
    }

    fun setOnProcessListener(func: (process: Int) -> Unit) {
        processListener = func
    }

    fun setOnResultListener(func: (msg: String) -> Unit) {
        resultListener = func
    }

    override fun run() {
        checkWifi()
        while (!mCancel) {
            try {
                val packet = DatagramPacket(ByteArray(RECEIVE_SIZE), RECEIVE_SIZE)
                mSocketClient.receive(packet)
                val receiveInfo = packet.data ?: ByteArray(RECEIVE_SIZE) { 0 }
                if (mCheck) {
                    val okResult = "ZNIOT_Air_A2".toByteArray()
                    val result = receiveInfo.copyOfRange(0, okResult.size)
                    if (result.contentEquals(okResult)) {
                        mCheck = false
                        keyAlive()
                        sendData()
                    } else {
                        cancel()
                        resultListener?.invoke("设备WIFI模块故障")
                    }
                } else {
                    val equalCrc = mCrcArray.contentEquals(receiveInfo.copyOfRange(13, 15))
                    val equalOkResult = receiveInfo.copyOfRange(0, 16).contentEquals(mOkResult)
                    if (equalCrc || equalOkResult) {
                        mRetry = 0
                        mSendIndex++
                        processListener?.invoke(mSendIndex * 100 / mSendSize)
                        sleep(if (equalCrc) 200 else 500)
                        when {
                            mSendIndex < mSendSize -> sendData()
                            else -> {
                                val buf = "AE5AA50010A0040102030405010203040506B10400FFFFE11E".hexToByteArray()
                                mSocketClient.send(DatagramPacket(buf, buf.size, InetAddress.getByName(mServer), SERVER_PORT))
                                mCancel = true
                                resultListener?.invoke("菜单更新完成,设备重启中...")
                            }
                        }
                    } else {
                        sendData()
                    }
                }
            } catch (e: SocketTimeoutException) {
                if (mRetry < 3) {
                    mRetry++
                    sendData()
                } else {
                    cancel()
                    resultListener?.invoke("设备响应超时,更新失败!")
                }
            }
        }
        mSocketClient.close()
    }

    private fun checkWifi() {
        try {
            val buf = "checkip".toByteArray()
            mSocketClient.send(DatagramPacket(buf, buf.size, InetAddress.getByName(mServer), SERVER_PORT))
        } catch (e: Exception) {
            checkWifi()
        }
    }

    private fun keyAlive() {
        try {
            val buf = "AE5AA50004F1A10100FFFFE11E".toByteArray()
            mSocketClient.send(DatagramPacket(buf, buf.size, InetAddress.getByName(mServer), SERVER_PORT))
        } catch (e: Exception) {
            keyAlive()
        }
    }


    private fun sendData() {
        try {
            val menuItem = mList[mSendIndex]

            val cmdList = mutableListOf<Byte>()
            cmdList.addAll("0024A004".hexToByteArray().toList())
            val deviceId = java.lang.Long.toHexString("708901880000".toLong())
            cmdList.addAll(deviceId.hexToByteArray().toList())
            cmdList.addAll("01020302".hexToByteArray().toList())
            cmdList.add(menuItem.id.toByte())
            cmdList.addAll("01".hexToByteArray().toList())
            cmdList.add(menuItem.temputer.toByte())
            cmdList.addAll((menuItem.time * 60).to2HByteArray().toList())
            val name = menuItem.name.toByteArray(charset("GB2312"))
            cmdList.addAll(name.copyInto(20, 32).toList())

            val crcList = mutableListOf<Byte>()
            crcList.addAll("AEE11E".hexToByteArray().toList())
            crcList.addAll(cmdList)
            mCrcArray = CRCUtil.getCrc16(crcList.toByteArray(), crcList.size).to2HByteArray()

            cmdList.addAll(mCrcArray.toList())
            cmdList.addAll("E11E".hexToByteArray().toList())

            val sendList = mutableListOf<Byte>()
            sendList.addAll("AE5AA5".hexToByteArray().toList())
            sendList.addAll(cmdList)

            val buf = sendList.toByteArray()
            mSocketClient.send(DatagramPacket(buf, buf.size, InetAddress.getByName(mServer), SERVER_PORT))
        } catch (e: Exception) {
            sendData()
        }
    }
}